package com.gitee.wsl.android.ui.signature.pan

import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.view.MotionEvent
import com.gitee.wsl.common.ui.base.ControlPoint
import kotlin.math.exp
import kotlin.math.hypot
import kotlin.math.ln


/***
 * 触摸点信息
 *
 * @since 2018/06/15
 * @author king
 */
class MotionElement(
    var x: Float,
    var y: Float,
    /**
     * 压力值  物理设备决定的，和设计的设备有关系
     */
    var pressure: Float,
    /**
     * 绘制的工具是否是手指或者是笔（触摸笔）
     */
    var toolType: Int
)

/**
 * 画笔基类
 *
 * @author king
 * @since 2018/06/15
 */
abstract class BasePen {
    protected var mHWPointList: ArrayList<ControlPoint> = ArrayList()
    protected var mLastPoint = ControlPoint(0f, 0f)
    protected var mPaint: Paint? = null

    /**
     * 笔的宽度信息
     */
    private var mBaseWidth = 0.0f
    private var mLastVel = 0.0f
    private var mLastWidth = 0.0f
    protected var mBezier: Bezier = Bezier()
    protected var mCurPoint: ControlPoint? = null

    fun setPaint(paint: Paint) {
        mPaint = paint
        mBaseWidth = paint.strokeWidth
    }

    fun draw(canvas: Canvas) {
        mPaint?.setStyle(Paint.Style.FILL)
        //点的集合少 不去绘制
        if (mHWPointList.size < 1) {
            return
        }
        mCurPoint = mHWPointList[0]
        doPreDraw(canvas)
    }

    private var lastId = 0 //记录最先/最后的手指id
    fun onTouchEvent(event: MotionEvent, canvas: Canvas): Boolean {
        // event会被下一次事件重用，这里必须生成新的，否则会有问题
        val action = event.action and event.actionMasked
        val event2 = MotionEvent.obtain(event)
        when (action) {
            MotionEvent.ACTION_DOWN -> {
                lastId = event2.getPointerId(0)
                onDown(createMotionElement(event2), canvas)
                return true
            }

            MotionEvent.ACTION_POINTER_DOWN -> {
                lastId = 0
                mLastVel = 0.0f
                mLastPoint = ControlPoint(
                    event2.getX(event2.actionIndex),
                    event2.getY(event2.actionIndex)
                )
            }

            MotionEvent.ACTION_MOVE -> {
                if (lastId != event2.getPointerId(event2.actionIndex)) {
                    return true
                }
                onMove(createMotionElement(event2), canvas)
                return true
            }

            MotionEvent.ACTION_POINTER_UP -> {
                onUp(createMotionElement(event2), canvas)
                return true
            }

            MotionEvent.ACTION_UP -> {
                lastId = event2.getPointerId(0)
                onUp(createMotionElement(event2), canvas)
                return true
            }

            else -> {}
        }
        return false
    }

    /**
     * 按下的事件
     */
    fun onDown(mElement: MotionElement, canvas: Canvas) {
        if (mPaint == null) {
            throw NullPointerException("paint不能为null")
        }
        if (getNewPaint(mPaint) != null) {
            var paint: Paint? = getNewPaint(mPaint)
            mPaint = paint
            paint = null
        }
        mHWPointList.clear()
        //记录down的控制点的信息
        val curPoint = ControlPoint(mElement.x, mElement.y)
        mLastWidth = 0.7f * mBaseWidth
        //down下的点的宽度
        curPoint.width = mLastWidth
        mLastVel = 0.0f
        //记录当前的点
        mLastPoint = curPoint
    }

    protected fun getNewPaint(paint: Paint?): Paint? {
        return null
    }

    /**
     * 手指移动的事件
     */
    fun onMove(mElement: MotionElement, canvas: Canvas) {
        val curPoint = ControlPoint(mElement.x, mElement.y)
        val deltaX = curPoint.x - mLastPoint.x
        val deltaY = curPoint.y - mLastPoint.y
        //deltaX和deltay平方和的二次方根 想象一个例子 1+1的平方根为1.4 （x²+y²）开根号
        //同理，当滑动的越快的话，deltaX+deltaY的值越大，这个越大的话，curDis也越大
        val curDis = hypot(deltaX, deltaY)
        //我们求出的这个值越小，画的点或者是绘制椭圆形越多，这个值越大的话，绘制的越少，笔就越细，宽度越小
        val curVel: Float = curDis * DIS_VEL_CAL_FACTOR
        val curWidth: Float
        //点的集合少，我们得必须改变宽度,每次点击的down的时候，这个事件
        if (mHWPointList.size < 2) {
            curWidth = calcNewWidth(
                curVel, mLastVel, curDis, 1.7f,
                mLastWidth
            )
            curPoint.width = curWidth
            mBezier.init(mLastPoint, curPoint)
        } else {
            mLastVel = curVel
            curWidth = calcNewWidth(
                curVel, mLastVel, curDis, 1.7f,
                mLastWidth
            )
            curPoint.width = curWidth
            mBezier.addNode(curPoint)
        }
        //每次移动的话，这里赋值新的值
        mLastWidth = curWidth
        doMove(curDis)
        mLastPoint = curPoint
    }

    /**
     * 手指抬起来的事件
     */
    fun onUp(mElement: MotionElement, canvas: Canvas) {
        if (mHWPointList.size == 0) {
            return
        }
        mCurPoint = ControlPoint(mElement.x, mElement.y)
        val deltaX = mCurPoint!!.x - mLastPoint.x
        val deltaY = mCurPoint!!.y - mLastPoint.y
        val curDis = hypot(deltaX, deltaY)
        mCurPoint!!.width = 0f
        mBezier.addNode(mCurPoint!!)
        val steps = 1 + curDis.toInt() / STEP_FACTOR
        val step = 1.0f / steps
        run {
            var t = 0.0f
            while (t < 1.0) {
                val point: ControlPoint = mBezier.getPoint(t)
                mHWPointList.add(point)
                t += step
            }
        }
        mBezier.end()
        var t = 0.0f
        while (t < 1.0f) {
            val point: ControlPoint = mBezier.getPoint(t)
            mHWPointList.add(point)
            t += step
        }
        draw(canvas)
        clear()
    }

    /**
     * 计算新的宽度信息
     */
    fun calcNewWidth(
        curVel: Float, lastVel: Float, curDis: Float,
        factor: Float, lastWidth: Float
    ): Float {
        val calVel = curVel * 0.6f + lastVel * (1 - 0.6f)
        val vfac = ln(factor * 2.0f) * -calVel
        return mBaseWidth * exp(vfac)
    }

    /**
     * 创建触摸点信息
     */
    fun createMotionElement(motionEvent: MotionEvent): MotionElement {
        return MotionElement(
            motionEvent.getX(0), motionEvent.getY(0),
            motionEvent.pressure, motionEvent.getToolType(0)
        )
    }

    /**
     * 清除缓存的触摸点
     */
    fun clear() {
        mHWPointList.clear()
    }

    /**
     * 绘制
     * 当现在的点和触摸点的位置在一起的时候不用去绘制
     */
    protected fun drawToPoint(canvas: Canvas, point: ControlPoint, paint: Paint) {
        if (mCurPoint?.x == point.x && mCurPoint?.y == point.y) {
            return
        }
        doDraw(canvas, point, paint)
    }

    /**
     * 判断笔是否为空
     */
    val isNullPaint: Boolean
        get() = mPaint == null

    /**
     * 移动的时候的处理方法
     */
    protected abstract fun doMove(f: Float)

    /**
     * 绘制方法
     */
    protected abstract fun doDraw(canvas: Canvas, point: ControlPoint, paint: Paint)

    /**
     * onDraw之前的操作
     */
    protected abstract fun doPreDraw(canvas: Canvas)

    companion object {
        /**
         * 画笔大小等级
         */
        var PAINT_SIZE_LEVEL = 2

        /**
         * 画笔颜色
         */
        var PAINT_COLOR: Int = Color.RED

        /**
         * 笔锋控制值,越小笔锋越粗,越不明显
         */
        const val DIS_VEL_CAL_FACTOR = 0.008f
        /**
         * 绘制计算的次数，数值越小计算的次数越多
         */
        const val STEP_FACTOR = 20
    }
}
